F5-TTS – Ich habe mir selbst meine Stimme geklaut

Frage: „Wie macht man seiner Mutter an Halloween so richtig Angst?“ Antwort: man pimpt ihren Sprachassistenten mit der eigenen Stimme und ändert das Keyword auf seinen Namen 😉

F5-TTS – Ich habe mir selbst meine Stimme geklaut

Herzlich willkommen in der schönen neuen Welt, in der nicht einmal Eure Stimme noch Euch gehört!  Der Diebstahl ist erschreckend einfach noch dazu.

Zuerst einmal installiert Ihr die nötige Software für die Stimmsynthese:

pip install git+https://github.com/SWivid/F5-TTS.git

Dann zieht Ihr euch von hier: https://huggingface.co/marduk-ra/F5-TTS-German/tree/main alle *safetensors und vocab.txt Dateien runter und parkt die auf Eurer Platte.

15 GB später braucht Ihr nur noch das hier starten: f5-tts_infer-gradio

10 Sekunden reichen zum Stimmenraub aus

Ihr bekommt dann dieses Webinterface zusehen, wenn Ihr http://localhost:7860/ aufruft

F5 Interface mit Texteingabefenster , Modellauswahl und Ausgabeparametern. Unkonfiguriert.Damit Ihr eine Deutsche Aussprache bekommt, müßt Ihr zunächst das Custommodel aktivieren und jeweils die Pfade zu dem Tensor und dem Vocab File manuell in die Selectboxen eintragen, die da präsentiert werden. Das ist nicht intuitiv zu verstehen, klickt einfach auf die Box und schreibt was hin.

Als nächstes macht Ihr Audacity auf und nehmt einen Satz auf, für den Ihr 10-15 Sekunden braucht. Den speichert Ihr auf Eurer Platte als WAV File ab und in einer Textdatei schreibt Ihr den Satz auf.

Nun könnt Ihr bei „Text to Generate“ den gewünschten Text eingeben und drückt dann auf „Synthesize„. Es rattert dann etwas im Grafikkartenlüfter und X Sekunden später wirft er Euch unten das erzeugt Sprachfile ab und Ihr könnt es abspielen oder abspeichern.

Wie macht man jetzt eine Sprachausgabe daraus?

Als erstes bauen wir uns einen Bash-Wrapper, der eine Korrekturen am Text vornimmt und das Caching der Audiofiles managed, damit man den gleichen Satz nicht immer wieder neu erzeugen muß:

#!/bin/bash

NOCACHE=0

if [ "$1" == "--nocache" ]; then
 NOCACHE=1
 shift
fi

TEXT=$(echo "$2" | sed -r 's/\.([^ $])/\1/g' | sed -r 's/,([^ $])/ komma \1/g')

if ! [[ "$TEXT" =~ !$ ]]; then
 if ! [[ "$TEXT" =~ \?$ ]]; then
  if ! [[ "$TEXT" =~ \.$ ]]; then
   TEXT="$TEXT."
  fi
 fi
fi

HASH=$(echo "$1$TEXT"| sha256sum | sed -e "s/ .*$//g")

echo "$HOME/.cache/pva/audio/$HASH.mp3"

if [ -e $HOME/.cache/pva/audio/$HASH.mp3 ];then

 play $HOME/.cache/pva/audio/$HASH.mp3 tempo 1 >/dev/null 2>/dev/null

else

 port=$(env LANG=C netstat -lnap 2>/dev/null| grep -c 127.0.0.1:7860.*LISTEN)
 if [ "$port" -eq 0 ]; then
  echo "starting F5-TTS service"
  f5-tts_infer-gradio & 2>&1 1>/dev/null
  sleep 16
 fi

 genfile=$(f5tts.py $1 "$2" |grep -v -E "(Loaded|ref_text|gen_text)" )

 if [ "$NOCACHE" -eq "0" ]; then 
  lame -V 5 -b 64 -B 224 $genfile $HOME/.cache/pva/audio/$HASH.mp3 >/dev/null 2>/dev/null
 fi
 rm -f $genfile

fi

Die grünmarkierten Stellen müßt Ihr ggf. anpassen, weil das bei Euch mit dem Start schneller oder langsamer geht und Ihr vielleicht auch einen anderen Namen für das folgende Pythonprogramm haben wollt:

#!/usr/bin/python3

from gradio_client import Client, handle_file

import sys
import os

n = len(sys.argv)

if ( n == 2 ):
	audio=handle_file('/home/marius/Programme/F5-TTS/Stimmen/default.wav')
	file = open('/home/marius/Programme/F5-TTS/Stimmen/default.txt',"r")
	rtext= file.readline()
	file.close()
	text=sys.argv[1]
else:
	audio=handle_file('/home/marius/Programme/F5-TTS/Stimmen/'+ sys.argv[1] +'.wav')
	file = open('/home/marius/Programme/F5-TTS/Stimmen/'+ sys.argv[1] +'.txt',"r")
	rtext= file.readline()
	file.close()
	text=sys.argv[2]

client = Client("http://127.0.0.1:7860/")
result = client.predict(
		new_choice="F5-TTS",
		api_name="/switch_tts_model"
)
# print(result)
result = client.predict(
		custom_ckpt_path="/home/marius/Programme/F5-TTS/marduk-german/f5_tts_german_1010000.safetensors",
		custom_vocab_path="/home/marius/Programme/F5-TTS/marduk-german/vocab.txt",
		api_name="/set_custom_model"
)

# print(result)
result = client.predict(
		ref_audio_input=audio,
		ref_text_input=rtext,
		gen_text_input=text,
		remove_silence=False,
		cross_fade_duration_slider=0.15,
		speed_slider=1,
		api_name="/basic_tts"
)
print(result[0])
os.system( "play '"+ result[0] +"' 2>/dev/null");

Das Pythonprogramm nimmt zwei Argumente an: Stimmenbasisname und den Text den man sprechen will.

Das mit den Basisnamen ist leicht erklärt:

$ ls -la Programme/F5-TTS/Stimmen/
insgesamt 48032
drwxr-xr-x.  2 marius marius     4096 16. Dez 21:19  .
drwxrwxr-x. 15 marius marius     4096 12. Dez 14:47  ..
-rw-rw-r--.  1 marius marius      184  9. Dez 18:10  default.txt
-rw-r--r--.  1 marius marius  1772176  9. Dez 18:10  default.wav
-rw-r--r--.  1 marius marius      136  7. Dez 23:14  DMorty.txt
-rw-r--r--.  1 marius marius  1159248  7. Dez 20:17  DMorty.wav
-rw-r--r--.  1 marius marius      179  7. Dez 20:23  DRick.txt
-rw-r--r--.  1 marius marius  1628108  7. Dez 20:24  DRick.wav
-rw-rw-r--.  1 marius marius      109  9. Dez 17:20  Marius.txt
-rw-------.  1 marius marius  1003864  9. Dez 18:35  Marius.wav

Ein Pärchen WAV und TXT Datei hat einfach den gleichen Anfangsnamen. So brauch man sich keine Dateinamen zu übergeben. Ihr setzt jetzt noch die Ausführungsrechte und seid fertig.

Verbesserungspotential

Ab und zu müßt Ihr mal was doppelt schreiben, weil das einfach weggelassen wird.

Wenn man ein gutes Ergebnis haben will, muß man auch eine Aufnahme der Stimme in  Studioqualität nehmen.

„Viel“ hilft hier nicht weiter. 15s sind echt Max.

Die Zukunft wird schlimmer

Leider muß ich Euch jetzt viel Spaß wünschen, weil es einfach irre lustig war damit rumzuspielen. „Leider“ hat da soviel Missbrauchspotential, daß einem schlecht wird, wenn man mal darüber nachdenkt. Alleine schon in Firmen wird das beim Social Engineering so richtig einschlagen, weil man jetzt am Telefon die Stimme einer Person aus dem Unternehmen benutzen kann. Auch im Bankwesen ist der eine Katastrophe: die Nord/LB z.b. will eine Stimmbestätigung, wenn ein Sachbearbeiter im Auftrag des anrufenden Kunden was ändern soll. Jetzt ratet mal , was man da sagen soll : „Ja ich will, daß Sie … ändern.“  Mit F5 ist das ja jetzt wohl ein Witz als Absicherung.

Es wird ernsthaft vorgeschlagen ein Sicherheits/Erkennungswort in Familien auszuhandeln, um am Telefon Betrüger leichter zu entlarven!

Mahnende letzte Worte

1. Mit Eurer Stimme könnt ihr machen was Ihr wollt.
2. Mit Stimmen von anderen Menschen könnt Ihr für Euch zu Hause rumspielen, aber öffentlich zur Schaustellen dürfte mit ziemlicher Sicherheit ein Problem darstellen, wenn die Kopie sehr gut ist.
3. Die Stimme eines Dritten zu benutzen um andere zu schädigen, stellt garantiert eine doppelte Straftat dar.

4. Das Sicherheitswort „Seegurke“ ist schon von allen anderen Rick&Morty Fans belegt, denkt Euch was eigenes aus 😉

Links:

[1] https://github.com/SWivid/F5-TTS
[2] https://huggingface.co/marduk-ra/F5-TTS-German/tree/main

Bitte macht das nicht nach – Bughunting mit Tools

Vor ein paar Tagen gab es die Nachricht, daß u.a. Curl mit ChatGPT generierten Fehlerberichten zu gespammt wird, die nur völlig nutzlose Zeitverschwendung darstellt, weil die Tools Sachen falsch einschätzen. Das ist Exim jetzt auch passiert.

Bitte macht das nicht nach – Bughunting mit Tools

An sich ist es eine super Sache, daß sich Menschen um die Sicherheit Ihrer Produkte Gedanken machen und aktiv helfen wollen, bei Problemen einzugreifen. Leider machen einige Tools den Entwicklern von OpenSource Projekten das Leben schwer ( siehe Curl ).

Auch wenn im Pullrequest kein ChatGPT drin war, mußte sich jemand heute um so eine Zeitverschwendungsmeldung kümmern. In diesem Fall war das Ich und das Projekt um das es ging Exim:

Damit alle Verstehen um was es geht:

Exim ist in C programmiert, eine Sprache die schon in den 70er benutzt wurde. Dem entsprechend sind die Standard-Funktionen extrem „schmal“ und ohne Rechenzeitverbrennung programmiert worden, meint: Geschwindigkeit war wichtiger als Sicherheit, weil es „noch keine Exploits gab“ und, hauptsächlich, die CPUs weniger leisten konnten.

strcpy() ist eine eingebaute C-Funktion die Inhalte von Speicher A in Speicher B kopiert, bis diese auf eine 0 trifft.  Die binäre Null also 0x00 gilt in C als Stringterminator, also das Ende eines Textstrings. (Das ging genau so lange gut, bis UTF kam , aber das ist eine andere Geschichte.) strcpy(ZIEL,QUELLE) kopiert also genau solange Bytes von der Quelle ins Ziel, bis ein 0x00 kommt, EGAL ob der Speicherbereich „ZIEL“ ausreichend groß ist, um die kopierte Datenanzahl aufzunehmen. Wenn er es nicht ist, kommt es zum BUFFER-OVERFLOW und damit zu potentiellen Remote-Code-Executions, aber meistens nur zum Programmabsturz.

strncpy() ist eine Funktion, die genau das gleiche macht wie strcpy() nur, daß fest vorgegeben wird, wie viel kopiert werden kann, was das überschreiben des Zielspeichers verhindert, außer man gibt da einfach quatsch als Länge an, dann natürlich nicht mehr 😉

Beim Pullrequest im Eximsource gings um

  if ((int)strlen(pw->pw_dir) + (int)strlen(filename) + 1 > sizeof(buffer))
    {
    printf("exim_lock: expanded file name %s%s is too long", pw->pw_dir,
      filename);
    exit(EXIT_FAILURE);
    }

  strcpy(buffer, pw->pw_dir);
  strcat(buffer, filename);
  filename = buffer;
}

. Das sollte so ersetzt werden:

– strcpy(buffer, pw->pw_dir);
+ strncpy(buffer, pw->pw_dir);

Leider braucht strncpy() 3 Argumente, nämlich auch die Größe des Zielspeichers:

char *strncpy(char dst[restrict .dsize], const char *restrict src, size_t dsize);

Wenn man da nur 2 Argumente liefert, schlägt das Kompilieren des Sources fehl, außer irgendeine Compiler Magie erkennt die Funktion und füllt den dritten Parameter heimlich auf. Sich darauf zu verlassen ist genauso fahrlässig, wie noch strcpy() zu benutzen, ohne vorher geprüft zu haben, ob das sicher klappen kann.

Die Eximdevs haben das aber bereits richtig gemacht:

  if ((int)strlen(pw->pw_dir) + (int)strlen(filename) + 1 > sizeof(buffer))
    {
    printf("exim_lock: expanded file name %s%s is too long", pw->pw_dir,
      filename);
    exit(EXIT_FAILURE);
    }

  strcpy(buffer, pw->pw_dir);
  strcat(buffer, filename);
  filename = buffer;
}

Der Test steht nur,Sourcecode bedingt, etwas weiter davon weg, weil man keine If-Than-Else Anweisung benutzt hat, sondern eine If-Not-Error Anweisung gemacht hat.  Es wird also der Fehlerfall abgefragt und dann ein Fehler ausgegeben,statt abzufragen ob das funktionieren kann, es dann zu tun und ANSONSTEN einen Fehler auszugeben.

Das ist völlig legitim, auch wenn ich persönlich das nicht so gemacht hätte, weil es dem Compiler erlaubt, etwas einfacheren Programmcode zu erzeugen.

Bitte nicht nachmachen

Wie Ihr oben selbst sehen könnt, ist alles ok gewesen. Irgendein Tool wird über strcpy() gestolpert sein und das routinemäßig der Person vor dem Monitor mitgeteilt haben. Die hatte aber keine Lust nachzusehen, ob überhaupt ein Problem vorliegt, oder keinen Plan davon, wie C funktioniert ( übrigens nicht meine Lieblingssprache 😉 ) und es dann einfach gemeldet.

Bitte macht das nicht nach! Das kostet uns als Entwickler einfach nur unnötige Zeit. Nehmt das Ergebnis und analysiert selbst nach, ob es überhaupt ein Problem gibt. Da haben alle was von, weil Ihr lernt was dazu, egal wie die Sache ausgeht, und ggf. habt Ihr auch ein Problem gefunden, das wirklich gelöst werden muß.

Beispiel:

Wenn ich …

char *ziel[500];
char *quellel[200];

memset(quelle,0,200);
strcpy(ziel,quelle);
return;

… habe, würde so ein Tool das strcpy() reklamieren, es könnte aber NIEMALS ein Problem darstellen. Wer als Mensch den Code sieht, versteht das auch, das Tool hat aber nur „grep ’strcpy(‚ sourcefile.c“ gemacht. Zum Problem wird so etwas hier:

function a(char* quelle) {

  char *ziel[500];
  strcpy(ziel,quelle);
  return;
}

Weil nichts über die Quelle, deren wahre Länge und Inhalt usw. bekannt ist. Hier muß man jetzt tatsächlich mit strncpy() arbeiten, um seinen Speicherbereich zu schützen:

function a(char* quelle) {

  char *ziel[500];
  strncpy(ziel,quelle,sizeof(ziel));
  return;
}

Und jetzt viel Spaß beim C lernen 😉

 

Scam: Überprüfung Ihrer Rundfunkgebühren-Rückerstattung – Jetzt handeln!

Schön ist es eine Spam-Fallenadresse zu haben, so daß man gar nicht nachdenken muß, ob eine Spam auch eine Spam ist und oder auch noch ein Scam. Für alle, die keine eigene Falle  haben, folgende Masche ging uns ins Netz:

Scam: Überprüfung Ihrer Rundfunkgebühren-Rückerstattung – Jetzt handeln!

Seit einigen Tagen kommen gehäuft Scammails mit dieser Masche rein:

Sehr geehrte/r ##EMAILADRESSE##,

Wir haben festgestellt, dass eine Überzahlung in Höhe von 37,72 € für Ihre Rundfunkgebühren vorliegt.

Um die Rückerstattung zu erhalten, bitten wir Sie, den untenstehenden Button zu klicken und den Rückerstattungsprozess abzuschließen.

Rückerstattung jetzt prüfen
Bei Fragen oder Unklarheiten stehen wir Ihnen unter support@beitragsservice.de gerne zur Verfügung.

Mit freundlichen Grüßen,
Ihr ARD ZDF Deutschlandradio Beitragsservice

In Real sieht das so aus:

An der Absendeadresse ist schon eindeutig erkennbar, daß es sich um eine gefälschte Email handelt.

Wie immer, weg mit dem Datenmüll 😉